//
// Created by root on 6/24/17.
//

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <regex.h>
#include "utils.h"
#include "constants.h"
#include "cmemory.h"

int match_regex(char* regex, char* text) {
    regex_t regex_comp;
    int reti;
    char msgbuf[100];

    reti = regcomp(&regex_comp, regex, REG_EXTENDED);
    if (reti) {
        fprintf(stderr, "Could not compile regex\n");
        return FALSE;
    }

    reti = regexec(&regex_comp, text, 0, NULL, 0);
    if (!reti) {
        regfree(&regex_comp);
        return TRUE;
    }
    else if (reti == REG_NOMATCH) {
        regfree(&regex_comp);
        return FALSE;
    }
    else {
        regerror(reti, &regex_comp, msgbuf, sizeof(msgbuf));
        fprintf(stderr, "Regex match failed: %s\n", msgbuf);
        regfree(&regex_comp);
        return FALSE;
    }
}

char** match_regex_groups(char* regex, int groups, char* text) {
    regex_t regex_comp;
    regmatch_t group[groups];
    int reti;
    char msgbuf[100];
    reti = regcomp(&regex_comp, regex, REG_EXTENDED);
    if (reti) {
        fprintf(stderr, "Could not compile regex\n");
        return NULL;
    }

    reti = regexec(&regex_comp, text, groups, group, 0);
    if (!reti) {
        char* *result = (char**)sys_malloc((groups + 1) * sizeof(char *));
        unsigned int g = 0;
        for (g = 0; g < groups; g++) {
            if (group[g].rm_so == (size_t)-1)
                break;  // No more groups

            char sourceCopy[strlen(text) + 1];
            strcpy(sourceCopy, text);
            sourceCopy[group[g].rm_eo] = 0;
            *(result + g) = strdup(sourceCopy + group[g].rm_so);
        }
        *(result + g) = NULL;

        regfree(&regex_comp);
        return result;
    }
    else if (reti == REG_NOMATCH) {
        regfree(&regex_comp);
        return NULL;
    }
    else {
        regerror(reti, &regex_comp, msgbuf, sizeof(msgbuf));
        fprintf(stderr, "Regex match failed: %s\n", msgbuf);
        regfree(&regex_comp);
        return NULL;
    }
}

char* concatenate(char* str, char* append) {
    if (append == NULL || strcmp(append, "") == 0) {
        return str;
    }
    int length = strlen(str) + strlen(append) + 1;
    str = realloc(str, length * sizeof(char));
    str = strcat(str, append);
    return str;
}

//file
void* read_file(char* inputFile, void* (*process_read)(FILE *in)) {
    void* result = NULL;
    FILE *fp = fopen(inputFile, "rb");
    if (!fp) {
        perror(inputFile);
        exit(EXIT_FAILURE);
    }
    result = process_read(fp);
    fclose(fp);
    return result;
}

void* write_file(char* outputFile, void* (*process_write)(FILE *out)) {
    void* result = NULL;
    FILE *fp = fopen(outputFile, "w");
    if (!fp) {
        perror(outputFile);
        exit(EXIT_FAILURE);
    }
    result = process_write(fp);
    fclose(fp);
    return result;
}

void* read_write_file(char* inputFile, char* outputFile, void* (*process_read_write)(FILE *in, FILE *out)) {
    void* result = NULL;
    FILE *fout = fopen(outputFile, "w");
    if (!fout) {
        perror(outputFile);
        exit(EXIT_FAILURE);
    }
    FILE *fin = fopen(inputFile, "rb");
    if (!fin) {
        perror(inputFile);
        exit(EXIT_FAILURE);
    }
    result = process_read_write(fin, fout);
    fclose(fin);
    fclose(fout);
    return result;
}

void* read_file_with_data(char* inputFile, void* data, void* (*process_read)(FILE *in, void* data)) {
    void* result = NULL;
    FILE *fp = fopen(inputFile, "rb");
    if (!fp) {
        perror(inputFile);
        exit(EXIT_FAILURE);
    }
    result = process_read(fp, data);
    fclose(fp);
    return result;
}

void* write_file_with_data(char* outputFile, void* data, void* (*process_write)(FILE *out, void* data)) {
    void* result = NULL;
    FILE *fp = fopen(outputFile, "w");
    if (!fp) {
        perror(outputFile);
        exit(EXIT_FAILURE);
    }
    result = process_write(fp, data);
    fclose(fp);
    return result;
}

void* read_write_file_with_data(char* inputFile, char* outputFile, void* data, void* (*process_read_write)(FILE *in, FILE *out, void* data)) {
    void* result = NULL;
    FILE *fout = fopen(outputFile, "w");
    if (!fout) {
        perror(outputFile);
        exit(EXIT_FAILURE);
    }
    FILE *fin = fopen(inputFile, "rb");
    if (!fin) {
        perror(inputFile);
        exit(EXIT_FAILURE);
    }
    result = process_read_write(fin, fout, data);
    fclose(fin);
    fclose(fout);
    return result;
}

int repetitions(FILE *stream, int c) {
    int count = 0;
    int ch;
    do {
        ch = fgetc(stream);
        if (ch != c) {
            ungetc(ch, stream);
            break;
        }
        else {
            count++;
        }
    } while (ch != EOF);
    return count;
}

int fpeek(FILE *stream) {
    int c = getc(stream);
    ungetc(c, stream);
    return c;
}

char* read_until(FILE *fp, int (*condition)(int c)) {
    int size = 128;
    int pos;
    int c;
    char *buffer = (char *)sys_malloc(size * sizeof(char));

    pos = 0;
    do {
        c = fgetc(fp);
        if (c != EOF) buffer[pos++] = (char) c;
        if (pos >= size - 1) { // increase buffer length - leave room for 0
            size *= 2;
            buffer = (char *) sys_realloc(buffer, size);
        }
    } while (c != EOF && condition(c));
    if (c != EOF) {
        pos--;
        ungetc(c, fp);
    }
    buffer[pos] = 0;
    return buffer;
}

char* read_line(FILE *fp) {
    int size = 512;
    int pos;
    int c;
    char *buffer = (char *)sys_malloc(size * sizeof(char));

    pos = 0;
    do { // read one line
        c = fgetc(fp);
        if (c != EOF) buffer[pos++] = (char) c;
        if (pos >= size - 1) { // increase buffer length - leave room for 0
            size *= 2;
            buffer = (char *) sys_realloc(buffer, size);
        }
    } while (c != EOF && c != '\n');
    buffer[pos] = 0;
    return buffer;
}

char** read_lines(FILE *fp) {
    int lines = 128;
    int size = 512;
    int c;
    int line = 0;
    char *buffer = (char *)sys_malloc(size * sizeof(char));
    char **result = (char **)sys_malloc(lines * sizeof(char *));

    do { // read all lines in file
        int pos = 0;
        do { // read one line
            c = fgetc(fp);
            if (c != EOF) buffer[pos++] = (char) c;
            if (pos >= size - 1) { // increase buffer length - leave room for 0
                size *= 2;
                buffer = (char *) sys_realloc(buffer, size);
            }
        } while (c != EOF && c != '\n');
        buffer[pos] = 0;
        // line is now in buffer
        //eval_line(sc, buffer, filePath);
        result[line++] = strdup(buffer);
        if (line >= lines - 1) { // increase buffer length - leave room for 0
            lines *= 2;
            result = (char **) sys_realloc(result, lines);
        }
    } while (c != EOF);
    result[line] = NULL;

    return result;
}

char *get_category(char *line, char categoryChar) {
    char *category = strdup(line);
    char *start = category;
    while(*start == categoryChar || *start == ' ') start++;
    category = start;
    while(*start != ' ' && *start != categoryChar) start++;
    *start = '\0';
    return category;
}